﻿#include "souistd.h"
#include "SApp.h"
#include "core/SNativeWnd.h"
#include "core/SWindowMgr.h"

#include "res.mgr/sfontpool.h"
#include "res.mgr/SUiDef.h"

#include "helper/STimerEx.h"
#include "helper/SScriptTimer.h"
#include "helper/SAutoBuf.h"
#include "helper/SToolTip.h"
#include "helper/SAppDir.h"
#include "helper/SwndFinder.h"
#include "helper/SHostMgr.h"

#include "control/Smessagebox.h"
#include "updatelayeredwindow/SUpdateLayeredWindow.h"
#include "helper/splitstring.h"
#include "res.mgr/SObjDefAttr.h"
#include "core/SSkin.h"
#include "control/souictrls.h"
#include "layout/SouiLayout.h"
#include "layout/SLinearLayout.h"
#include "layout/SGridLayout.h"
#include "animation/SInterpolatorImpl.h"
#include "core/SWndAccessible.h"

#include "msaa/SAccProxyWindow.h"
#include "msaa/SAccProxyCmnCtrl.h"

#include "animation/SAnimation.h"
#include "animation/SAnimationSet.h"
#include "animation/ScaleAnimation.h"
#include "animation/SRotateAnimation.h"
#include "animation/SAlphaAnimation.h"
#include "animation/STranslateAnimation.h"
#include "valueAnimator/SValueAnimator.h"


static const TCHAR KTypeBitmap[] = _T("BITMAP");
static const TCHAR KTypeCursor[] = _T("CURSOR");
static const TCHAR KTypeIcon[]  = _T("ICON");
static const  TCHAR KTypeHtml[] = _T("HTML");

const static WCHAR KNodeUidef[] = L"uidef";
const static WCHAR KNodeString[] = L"string";
const static WCHAR KNodeColor[] = L"color";
const static WCHAR KNodeDim[] = L"dim";
const static WCHAR KNodeSkin[] = L"skin";
const static WCHAR KNodeStyle[] = L"style";
const static WCHAR KNodeTemplate[] = L"template";
const static WCHAR KNodeObjAttr[] = L"objattr";
const static WCHAR KDefFontFace[] = L"Tahoma";

namespace SOUI {

    static pugi::xml_node GetSourceXmlNode(pugi::xml_node nodeRoot, pugi::xml_document &docInit,
                                           IResProvider *pResProvider, const wchar_t *pszName) {
        pugi::xml_node     nodeData = nodeRoot.child(pszName, false);
        if (nodeData) {
            pugi::xml_attribute attrSrc = nodeData.attribute(L"src", false);
            if (attrSrc) {
                //优先从src属性里获取数据
                SStringT strSrc = S_CW2T(attrSrc.value());
                SStringTList strList;
                if (2 == ParseResID(strSrc, strList)) {
                    SAutoBuf strXml;
                    size_t dwSize = pResProvider->GetRawBufferSize(strList[0], strList[1]);
                    
                    strXml.Allocate(dwSize);
                    pResProvider->GetRawBuffer(strList[0], strList[1], strXml, dwSize);
                    pugi::xml_parse_result result = docInit.load_buffer(strXml, strXml.size(), pugi::parse_default, pugi::encoding_auto);
                    if (result) {
                        nodeData = docInit.child(pszName);
                    }
                }
            }
        }
        return nodeData;
    }
    
    class SNullTranslator : public TObjRefImpl<ITranslatorMgr> {
      public:
        BOOL CreateTranslator(ITranslator **pLang) {
            return FALSE;
        }
        BOOL InstallTranslator(ITranslator *pLang) {
            return FALSE;
        }
        BOOL UninstallTranslator(const SStringW &id) {
            return FALSE;
        }
        int tr(const SStringW &strSrc, const SStringW &strCtx, wchar_t *pszOut, int nBufLen) const {
            return 0;
        }
        
        
        virtual void SetLanguage(const SStringW &strLang) {
        }
        
        virtual void GetLanguage(wchar_t szOut[TR_MAX_NAME_LEN]) const {
            szOut[0] = 0;
        }
        
    };
    
    class SDefToolTipFactory : public TObjRefImpl<IToolTipFactory> {
      public:
        /*virtual */
        IToolTip *CreateToolTip(HWND hHost) {
            STipCtrl *pTipCtrl = new STipCtrl;
            if(!pTipCtrl->Create()) {
                delete pTipCtrl;
                return NULL;
            }
            return pTipCtrl;
        }
        
        /*virtual */void DestroyToolTip(IToolTip *pToolTip) {
            if(pToolTip) {
                STipCtrl *pTipCtrl = (STipCtrl *)pToolTip;
                if (pTipCtrl->IsWindow()) {
                    pTipCtrl->DestroyWindow();
                }
            }
        }
    };
    
    class SDefMsgLoopFactory : public TObjRefImpl<IMsgLoopFactory> {
      public:
        virtual SMessageLoop *CreateMsgLoop() {
            return new SMessageLoop;
        }
        
        virtual void DestoryMsgLoop(SMessageLoop *pMsgLoop) {
            delete pMsgLoop;
        }
    };
    
    void SObjectDefaultRegister::RegisterWindows(SObjectFactoryMgr *objFactory) const {
        objFactory->TplRegisterFactory<SWindow>();
        objFactory->TplRegisterFactory<SPanel>();
        objFactory->TplRegisterFactory<SStatic>();
        objFactory->TplRegisterFactory<SButton>();
        objFactory->TplRegisterFactory<SImageWnd>();
        objFactory->TplRegisterFactory<SProgress>();
        objFactory->TplRegisterFactory<SImageButton>();
        objFactory->TplRegisterFactory<SLine>();
        objFactory->TplRegisterFactory<SCheckBox>();
        objFactory->TplRegisterFactory<SIconWnd>();
        objFactory->TplRegisterFactory<SRadioBox>();
        objFactory->TplRegisterFactory<SRadioGroup>();
        objFactory->TplRegisterFactory<SLink>();
        objFactory->TplRegisterFactory<SGroup>();
        objFactory->TplRegisterFactory<SAnimateImgWnd>();
        objFactory->TplRegisterFactory<SScrollView>();
        objFactory->TplRegisterFactory<SRealWnd>();
        objFactory->TplRegisterFactory<SToggle>();
        objFactory->TplRegisterFactory<SCaption>();
        objFactory->TplRegisterFactory<STabCtrl>();
        objFactory->TplRegisterFactory<STabPage>();
        objFactory->TplRegisterFactory<SActiveX>();
        objFactory->TplRegisterFactory<SFlashCtrl>();
        objFactory->TplRegisterFactory<SSplitPane>();
        objFactory->TplRegisterFactory<SSplitWnd>();
        objFactory->TplRegisterFactory<SSplitWnd_Col>();
        objFactory->TplRegisterFactory<SSplitWnd_Row>();
        objFactory->TplRegisterFactory<SSliderBar>();
        objFactory->TplRegisterFactory<STreeCtrl>();
        objFactory->TplRegisterFactory<SScrollBar>();
        objFactory->TplRegisterFactory<SHeaderCtrl>();
        objFactory->TplRegisterFactory<SListCtrl>();
        objFactory->TplRegisterFactory<SListBox>();
        objFactory->TplRegisterFactory<SRichEdit>();
        objFactory->TplRegisterFactory<SEdit>();
        objFactory->TplRegisterFactory<SHotKeyCtrl>();
        objFactory->TplRegisterFactory<SComboBox>();
        objFactory->TplRegisterFactory<SSpinButtonCtrl>();
        objFactory->TplRegisterFactory<SListView>();
        objFactory->TplRegisterFactory<SComboView>();
        objFactory->TplRegisterFactory<SMCListView>();
        objFactory->TplRegisterFactory<STileView>();
        objFactory->TplRegisterFactory<STreeView>();
        objFactory->TplRegisterFactory<SMenuBar>();
        objFactory->TplRegisterFactory<SCalendar>();
        objFactory->TplRegisterFactory<SDateTimePicker>();
    }
    
    void SObjectDefaultRegister::RegisterSkins(SObjectFactoryMgr *objFactory)  const {
        objFactory->TplRegisterFactory<SSkinImgList>();
        objFactory->TplRegisterFactory<SSkinImgCenter>();
        objFactory->TplRegisterFactory<SSkinImgFrame>();
        objFactory->TplRegisterFactory<SSkinImgFrame2>();
        objFactory->TplRegisterFactory<SSkinButton>();
        objFactory->TplRegisterFactory<SSkinGradation>();
        objFactory->TplRegisterFactory<SSkinScrollbar>();
        objFactory->TplRegisterFactory<SSkinColorRect>();
        objFactory->TplRegisterFactory<SSkinShape>();
        objFactory->TplRegisterFactory<SSKinGroup>();
    }
    
    void SObjectDefaultRegister::RegisterLayouts(SObjectFactoryMgr *objFactory)  const {
        objFactory->TplRegisterFactory<SouiLayout>();
        objFactory->TplRegisterFactory<SLinearLayout>();
        objFactory->TplRegisterFactory<SHBox>();
        objFactory->TplRegisterFactory<SVBox>();
        objFactory->TplRegisterFactory<SGridLayout>();
    }
    
    void SObjectDefaultRegister::RegisterInterpolator(SObjectFactoryMgr *objFactory)  const {
        objFactory->TplRegisterFactory<SLinearInterpolator>();
        objFactory->TplRegisterFactory<SAccelerateInterpolator>();
        objFactory->TplRegisterFactory<SDecelerateInterpolator>();
        objFactory->TplRegisterFactory<SAccelerateDecelerateInterpolator>();
        objFactory->TplRegisterFactory<SAnticipateInterpolator>();
        objFactory->TplRegisterFactory<SAnticipateOvershootInterpolator>();
        objFactory->TplRegisterFactory<SBounceInterpolator>();
        objFactory->TplRegisterFactory<SCycleInterpolator>();
        objFactory->TplRegisterFactory<SOvershootInterpolator>();
        objFactory->TplRegisterFactory<SPathInterpolator>();
    }
    
    void SObjectDefaultRegister::RegisterAnimation(SObjectFactoryMgr *objFactory) const {
        objFactory->TplRegisterFactory<SAnimation>();
        objFactory->TplRegisterFactory<SAnimationSet>();
        objFactory->TplRegisterFactory<SAlphaAnimation>();
        objFactory->TplRegisterFactory<SScaleAnimation>();
        objFactory->TplRegisterFactory<SRotateAnimation>();
        objFactory->TplRegisterFactory<STranslateAnimation>();
    }
    void SObjectDefaultRegister::RegisterValueAnimator(SObjectFactoryMgr *objFactory) const {
        objFactory->TplRegisterFactory<SIntAnimator>();
        objFactory->TplRegisterFactory<SFloatAnimator>();
        objFactory->TplRegisterFactory<SColorAnimator>();
    }
//////////////////////////////////////////////////////////////////////////
// SApplication

    template<> SApplication *SSingleton<SApplication>::ms_Singleton = 0;
    
    SApplication::SApplication(IRenderFactory *pRendFactory, HINSTANCE hInst, LPCTSTR pszHostClassName,
                               const ISystemObjectRegister &sysObjRegister, BOOL bImeApp)
        : m_hInst(hInst)
        , m_RenderFactory(pRendFactory)
        , m_hMainWnd(NULL) {
        SWndSurface::Init();
        memset(m_pSingletons, 0, sizeof(m_pSingletons));
        _CreateSingletons(hInst, pszHostClassName, bImeApp);
        m_translator.Attach(new SNullTranslator);
        m_tooltipFactory.Attach(new SDefToolTipFactory);
        m_msgLoopFactory.Attach(new SDefMsgLoopFactory);
        SAppDir appDir(hInst);
        m_strAppDir = appDir.AppDir();
        m_pMsgLoop = GetMsgLoopFactory()->CreateMsgLoop();
        AddMsgLoop(m_pMsgLoop);
        sysObjRegister.RegisterLayouts(this);
        sysObjRegister.RegisterSkins(this);
        sysObjRegister.RegisterWindows(this);
        sysObjRegister.RegisterInterpolator(this);
        sysObjRegister.RegisterAnimation(this);
        sysObjRegister.RegisterValueAnimator(this);
    }
    
    SApplication::~SApplication(void) {
        RemoveMsgLoop();
        GetMsgLoopFactory()->DestoryMsgLoop(m_pMsgLoop);
        _DestroySingletons();
    }
    
    void SApplication::_CreateSingletons(HINSTANCE hInst, LPCTSTR pszHostClassName, BOOL bImeApp) {
        //m_pSingletons[SUiDef::GetType()] = new SUiDef();
        m_pSingletons[SWindowMgr::GetType()] = new SWindowMgr();
        m_pSingletons[STimer2::GetType()] = new STimer2();
        m_pSingletons[SScriptTimer::GetType()] = new SScriptTimer();
        m_pSingletons[SFontPool::GetType()] = new SFontPool(m_RenderFactory);
        m_pSingletons[SStylePoolMgr::GetType()] =  new SStylePoolMgr();
        m_pSingletons[STemplatePoolMgr::GetType()] = new STemplatePoolMgr();
        m_pSingletons[SWindowFinder::GetType()] = new SWindowFinder();
        m_pSingletons[STextServiceHelper::GetType()] = new STextServiceHelper();
        m_pSingletons[SRicheditMenuDef::GetType()] = new SRicheditMenuDef();
        m_pSingletons[SNativeWndHelper::GetType()] =   new SNativeWndHelper(hInst, pszHostClassName, bImeApp);
        m_pSingletons[SHostMgr::GetType()] =   new SHostMgr();
    }
    
#define DELETE_SINGLETON(x) \
    delete (x*)m_pSingletons[x::GetType()];\
    m_pSingletons[x::GetType()] = NULL;
    
    void SApplication::_DestroySingletons() {
        DELETE_SINGLETON(SHostMgr);
        DELETE_SINGLETON(SNativeWndHelper);
        DELETE_SINGLETON(SRicheditMenuDef);
        DELETE_SINGLETON(STextServiceHelper);
        DELETE_SINGLETON(SWindowFinder);
        DELETE_SINGLETON(SStylePoolMgr);
        DELETE_SINGLETON(STemplatePoolMgr);
        DELETE_SINGLETON(SFontPool);
        DELETE_SINGLETON(SScriptTimer);
        DELETE_SINGLETON(STimer2);
        DELETE_SINGLETON(SWindowMgr);
        //DELETE_SINGLETON(SUiDef);
    }
    
    
    IAccProxy *SApplication::CreateAccProxy(SWindow *pWnd) const {
#ifdef SOUI_ENABLE_ACC
        if(pWnd->IsClass(SProgress::GetClassName())) {
            return new SAccProxyProgress(pWnd);
        } else if(pWnd->IsClass(SSliderBar::GetClassName())) {
            return new SAccProxySlideBar(pWnd);
        } else if(pWnd->IsClass(SButton::GetClassName()) || pWnd->IsClass(SImageButton::GetClassName())) {
            return new SAccProxyButton(pWnd);
        } else if(pWnd->IsClass(SComboBox::GetClassName())) {
            return new SAccProxyCombobox(pWnd);
        } else if(pWnd->IsClass(SRichEdit::GetClassName())) {
            return new SAccProxyEdit(pWnd);
        } else if(pWnd->IsClass(SCheckBox::GetClassName())) {
            return new SAccProxyCheckButton(pWnd);
        } else if(pWnd->IsClass(SRadioBox::GetClassName())) {
            return new SAccProxyRadioButton(pWnd);
        }
        return new SAccProxyWindow(pWnd);
#else
        return NULL;
#endif//SOUI_ENABLE_ACC
    }
    
    IAccessible *SApplication::CreateAccessible(SWindow *pWnd) const {
#ifdef SOUI_ENABLE_ACC
        return new SAccessible(pWnd);
#else
        return NULL;
#endif//SOUI_ENABLE_ACC
    }
    
    
    void *SApplication::GetInnerSingleton(int nType) {
        if (nType < 0 || nType >= SINGLETON_COUNT) {
            return NULL;
        }
        return m_pSingletons[nType];
    }
    
    BOOL SApplication::_LoadXmlDocment( LPCTSTR pszXmlName, LPCTSTR pszType, pugi::xml_document &xmlDoc,
                                        IResProvider *pResProvider/* = NULL*/) {
        if(!pResProvider) {
            if(IsFileType(pszType)) {
                pugi::xml_parse_result result = xmlDoc.load_file(pszXmlName, pugi::parse_default, pugi::encoding_auto);
                SASSERT_FMTA(result, "parse xml error! xmlName=%s,desc=%s,offset=%d", S_CT2A(pszXmlName).c_str(), result.description(),
                             result.offset);
                return result;
            } else {
                pResProvider = GetMatchResProvider(pszType, pszXmlName);
            }
        }
        
        if(!pResProvider) {
            return FALSE;
        }
        size_t dwSize = pResProvider->GetRawBufferSize(pszType, pszXmlName);
        if(dwSize == 0) {
            return FALSE;
        }
        SAutoBuf strXml;
        strXml.Allocate(dwSize);
        pResProvider->GetRawBuffer(pszType, pszXmlName, strXml, dwSize);
        pugi::xml_parse_result result = xmlDoc.load_buffer(strXml, strXml.size(), pugi::parse_default, pugi::encoding_auto);
        SASSERT_FMTA(result, "parse xml error! xmlName=%s,desc=%s,offset=%d", S_CT2A(pszXmlName).c_str(), result.description(),
                     result.offset);
        return result;
    }
    
    
    BOOL SApplication::LoadXmlDocment(pugi::xml_document &xmlDoc, const SStringT &strResId) {
        SStringTList strLst;
        if(2 != ParseResID(strResId, strLst)) {
            return FALSE;
        }
        return _LoadXmlDocment(strLst[1], strLst[0], xmlDoc);
    }
    
    BOOL SApplication::LoadBuffer(SAutoBuf &buffer, const SStringT &strResId) {
        SStringTList strLst;
        if (2 != ParseResID(strResId, strLst)) {
            return FALSE;
        }
        LPCTSTR pszXmlName = strLst[1];
        LPCTSTR pszType = strLst[0];
        auto pResProvider = GetMatchResProvider(pszType, pszXmlName);
        if (!pResProvider) {
            return FALSE;
        }
        size_t dwSize = pResProvider->GetRawBufferSize(pszType, pszXmlName);
        if (dwSize == 0) {
            return FALSE;
        }
        buffer.Allocate(dwSize);
        pResProvider->GetRawBuffer(pszType, pszXmlName, buffer, dwSize);
        return TRUE;
    }
    
    IAnimation *SApplication::LoadAnimation(const SStringT &strResId) {
        pugi::xml_document xml;
        if (!LoadXmlDocment(xml, strResId)) {
            return NULL;
        }
        IAnimation *pRet = CreateAnimationByName(xml.first_child().name());
        if (!pRet) {
            return NULL;
        }
        pRet->InitFromXml(xml.first_child());
        return pRet;
    }
    
    IValueAnimator *SApplication::LoadValueAnimator(const SStringT &strResId) {
        pugi::xml_document xml;
        if (!LoadXmlDocment(xml, strResId)) {
            return NULL;
        }
        IValueAnimator *pRet = CreateValueAnimatorByName(xml.first_child().name());
        if (!pRet) {
            return NULL;
        }
        pRet->InitFromXml(xml.first_child());
        return pRet;
    }
    
    
    bool SApplication::InitEnv(const SOUI::SStringW &envName, const SOUI::SStringW &themeType) {
    
        SOUI::CAutoRefPtr<SOUI::IResProvider>   pResProvider;
        SOUI::CreateResProvider(SOUI::RES_FILE, (SOUI::IObjRef **)&pResProvider);
        if (!pResProvider->Init((LPARAM)envName.c_str(), 0)) {
            return false;
        }
        
        m_pEnvResProvider = pResProvider;
        
        LPCTSTR pszUidef = _T("uidef:xml_init");
        BOOL bRet = FALSE;
        SStringTList strUiDef;
        if (2 != ParseResID(pszUidef, strUiDef)) {
            SLOGFMTD(_T("warning!!!! Add ResProvider Error."));
        }
        const wchar_t *defFontFace = KDefFontFace;
        size_t dwSize = pResProvider->GetRawBufferSize(strUiDef[0], strUiDef[1]);
        if (dwSize == 0) {
            SLOGFMTD(_T("warning!!!! uidef was not found in the specified resprovider"));
        } else {
            pugi::xml_document docInit;
            SAutoBuf strXml;
            strXml.Allocate(dwSize);
            
            pResProvider->GetRawBuffer(strUiDef[0], strUiDef[1], strXml, dwSize);
            pugi::xml_parse_result result = docInit.load_buffer(strXml, strXml.size(), pugi::parse_default, pugi::encoding_auto);
            
            if (!result) {
                //load xml failed
                SLOGFMTD(_T("warning!!! load uidef as xml document failed"));
            } else {
                //init named objects
                pugi::xml_node root = docInit.child(KNodeUidef, false);
                if (!root) {
                    SLOGFMTD(_T("warning!!! \"uidef\" element is not the root element of uidef xml"));
                } else {
                    //parse default font
                    pugi::xml_node xmlFont;
                    xmlFont = root.child(L"font", false);
                    
                    FONTSTYLE fontStyle(0);
                    if (xmlFont) {
                        fontStyle.attr.cSize = xmlFont.attribute(L"size").as_int(12);
                        fontStyle.attr.byCharset = (BYTE)xmlFont.attribute(L"charset").as_int(DEFAULT_CHARSET);
                        fontStyle.attr.fBold = xmlFont.attribute(L"bold").as_bool(false);
                        fontStyle.attr.fUnderline = xmlFont.attribute(L"underline").as_bool(false);
                        fontStyle.attr.fStrike = xmlFont.attribute(L"strike").as_bool(false);
                        fontStyle.attr.fItalic = xmlFont.attribute(L"italic").as_bool(false);
                        fontStyle.attr.byWeight = xmlFont.attribute(L"weight").as_uint(FW_NORMAL);
                        // xmlFont.attribute(L"weight").as_uint(FW_LIGHT);//
                        //  (xmlFont.attribute(L"weight").as_uint(0) + 2) / 4; //scale weight from [0-1000] to [0,250].
                        defFontInfo.style = fontStyle;
                        defFontInfo.strFaceName = xmlFont.attribute(L"face").value();
                        
                        if (defFontInfo.strFaceName.IsEmpty() ) {
                            defFontInfo.strFaceName = defFontFace;
                        }
                    } else {
                        fontStyle.attr.cSize = 12;
                        fontStyle.attr.byCharset = DEFAULT_CHARSET;
                        defFontInfo.style = fontStyle;
                        defFontInfo.strFaceName = defFontFace;
                    }
                    SFontPool::getSingletonPtr()->SetDefFontInfo(defFontInfo);
                    //parse default Unit
                    pugi::xml_node xmlUnit;
                    xmlUnit = root.child(L"unit");
                    if (xmlUnit) {
                        SStringW strUnit = xmlUnit.attribute(L"defUnit").as_string(L"px");
                        SLayoutSize::Unit unit = SLayoutSize::unitFromString(strUnit);
                        if (unit != SLayoutSize::unknow) {
                            SLayoutSize::setDefUnit(unit);
                        }
                    }
                    
                    xmlCaret.reset();
                    pugi::xml_node xmlCaretNode = root.child(L"caret");
                    if (xmlCaretNode) {
                        xmlCaret.append_copy(xmlCaretNode);
                    }
                    
                    //load named dimension
                    {
                        pugi::xml_document docData;
                        pugi::xml_node     nodeData = GetSourceXmlNode(root, docData, pResProvider, KNodeDim);
                        if (nodeData) {
                            m_namedDim.Init(nodeData);
                        }
                    }
                    
                    {
                        pugi::xml_document docData;
                        pugi::xml_node     nodeData = GetSourceXmlNode(root, docData, pResProvider, KNodeObjAttr);
                        if (nodeData) {
                            m_objDefAttr = new SObjDefAttr();
                            m_objDefAttr->Init(nodeData);
                        }
                    }
                    
                    //load named style
                    {
                        pugi::xml_document docData;
                        pugi::xml_node     nodeData = GetSourceXmlNode(root, docData, pResProvider, KNodeStyle);
                        if (nodeData) {
                            SOUI::SStylePoolMgr::getSingleton().Init(nodeData);
                        }
                    }
                    
                    //load named template
                    {
                        pugi::xml_document docData;
                        pugi::xml_node     nodeData = GetSourceXmlNode(root, docData, pResProvider, KNodeTemplate);
                        if (nodeData) {
                            SOUI::STemplatePoolMgr::getSingleton().Init(nodeData);
                        }
                    }
                    
                }
            }
        }
        return true;
    }
    
    int SApplication::Run( HWND hMainWnd ) {
        m_hMainWnd = hMainWnd;
        int nRet = m_pMsgLoop->Run();
        if(::IsWindow(m_hMainWnd)) {
            DestroyWindow(m_hMainWnd);
        }
        
        m_themeProviders.clear();
        return nRet;
    }
    
    HINSTANCE SApplication::GetInstance() {
        return m_hInst;
    }
    
    void SApplication::SetTranslator(ITranslatorMgr *pTrans) {
        m_translator = pTrans;
    }
    
    ITranslatorMgr *SApplication::GetTranslator() {
        return m_translator;
    }
    
    void SApplication::SetScriptFactory(IScriptFactory *pScriptFactory) {
        m_pScriptFactory = pScriptFactory;
    }
    
    
    HRESULT SApplication::CreateScriptModule( IScriptModule **ppScriptModule ) {
        if(!m_pScriptFactory) {
            return E_FAIL;
        }
        return m_pScriptFactory->CreateScriptModule(ppScriptModule);
    }
    
    IRenderFactory *SApplication::GetRenderFactory() {
        return m_RenderFactory;
    }
    
    void SApplication::SetRealWndHandler( IRealWndHandler *pRealHandler ) {
        m_pRealWndHandler = pRealHandler;
    }
    
    IRealWndHandler *SApplication::GetRealWndHander() {
        return m_pRealWndHandler;
    }
    
    IToolTipFactory *SApplication::GetToolTipFactory() {
        return m_tooltipFactory;
    }
    
    void SApplication::SetToolTipFactory( IToolTipFactory *pToolTipFac ) {
        m_tooltipFactory = pToolTipFac;
    }
    
    HWND SApplication::GetMainWnd() {
        return m_hMainWnd;
    }
    
    BOOL SApplication::SetMsgLoopFactory(IMsgLoopFactory *pMsgLoopFac) {
        if(m_msgLoopMap.GetCount() > 0) {
            return FALSE;
        }
        m_msgLoopFactory->DestoryMsgLoop(m_pMsgLoop);
        m_msgLoopFactory = pMsgLoopFac;
        m_pMsgLoop = m_msgLoopFactory->CreateMsgLoop();
        return TRUE;
    }
    
    IMsgLoopFactory *SApplication::GetMsgLoopFactory() {
        return m_msgLoopFactory;
    }
    
    void SApplication::InitXmlNamedID(const SNamedID::NAMEDVALUE *pNamedValue, int nCount, BOOL bSorted) {
        m_namedID.Init2(pNamedValue, nCount, bSorted);
    }
    
    int SApplication::Str2ID(const SStringW &str) {
        return m_namedID.String2Value(str);
    }
    
    
    SStringW SApplication::tr(const SStringW &strSrc, const SStringW &strCtx) const {
        int nRet = m_translator->tr(strSrc, strCtx, NULL, 0);
        if(nRet == 0) {
            return strSrc;
        }
        SStringW strRet;
        wchar_t *pBuf = strRet.GetBufferSetLength(nRet - 1);
        m_translator->tr(strSrc, strCtx, pBuf, nRet);
        strRet.ReleaseBuffer();
        return strRet;
    }
    
    SWindow *SApplication::CreateWindowByName(LPCWSTR pszWndClass) {
        //支持使用类似button.ok这样的控件名来创建控件，对于这种格式自动应用button.ok为class属性.
        SStringW strClsName = pszWndClass;
        int nPos = strClsName.ReverseFind(L'.');
        if (nPos != -1) {
            strClsName = strClsName.Left(nPos);
        }
        SWindow *pRet = (SWindow *)CreateObject(SObjectInfo(strClsName, Window));
        if (pRet && nPos != -1) {
            pRet->SetAttribute(L"class", pszWndClass, TRUE);
        }
        return pRet;
    }
    
    ISkinObj *SApplication::CreateSkinByName(LPCWSTR pszSkinClass)  {
        return (ISkinObj *)CreateObject(SObjectInfo(pszSkinClass, Skin));
    }
    
    IInterpolator *SApplication::CreateInterpolatorByName(LPCWSTR pszName)  {
        return (IInterpolator *)CreateObject(SObjectInfo(pszName, Interpolator));
    }
    
    IAnimation *SApplication::CreateAnimationByName(LPCWSTR pszName) {
        return (IAnimation *)CreateObject(SObjectInfo(pszName, Animation));
    }
    
    IValueAnimator *SApplication::CreateValueAnimatorByName(LPCWSTR pszName)  {
        return (IValueAnimator *)CreateObject(SObjectInfo(pszName, ValueAnimator));
    }
    
    void SApplication::SetLogManager(ILog4zManager *pLogMgr) {
        m_logManager = pLogMgr;
    }
    
    ILog4zManager *SApplication::GetLogManager() {
        return m_logManager;
    }
    
    SStringT SApplication::GetAppDir() const {
        return m_strAppDir;
    }
    
    void SApplication::SetAppDir(const SStringT &strAppDir) {
        m_strAppDir = strAppDir;
    }
    
    IAttrStorageFactory *SApplication::GetAttrStorageFactory() {
        return m_pAttrStroageFactory;
    }
    
    void SApplication::SetAttrStorageFactory(IAttrStorageFactory *pAttrStorageFactory) {
        m_pAttrStroageFactory = pAttrStorageFactory;
    }
    
    
    bool SApplication::addGlobalSkin(const SStringW &strSkinName, ISkinObj *pSkin, int nScale) {
        return m_globalSkinPool.addSkin(strSkinName, pSkin, nScale);
    }
    
    ISkinObj *SApplication::GetSkin(const SStringW &strSkinName, int nScale) {
        ISkinObj *pObj = m_globalSkinPool.GetSkin(strSkinName, nScale);
        if (pObj) {
            return pObj;
        }
        
        if (m_pCurSkinPool) {
            return m_pCurSkinPool->GetSkin(strSkinName, nScale);
        }
        return nullptr;
    }
    
    static const wchar_t *__BUILDIN_SKIN_NAMES[] = {
        L"_skin.sys.checkbox",
        L"_skin.sys.radio",
        L"_skin.sys.focuscheckbox",
        L"_skin.sys.focusradio",
        L"_skin.sys.btn",
        L"_skin.sys.scrollbar",
        L"_skin.sys.border",
        L"_skin.sys.dropbtn",
        L"_skin.sys.tree.toggle",
        L"_skin.sys.tree.checkbox",
        L"_skin.sys.tree.lines",
        L"_skin.sys.tab.page",
        L"_skin.sys.header",
        L"_skin.sys.split.vert",
        L"_skin.sys.split.horz",
        L"_skin.sys.prog.bkgnd",
        L"_skin.sys.prog.bar",
        L"_skin.sys.vert.prog.bkgnd",
        L"_skin.sys.vert.prog.bar",
        L"_skin.sys.slider.thumb",
        L"_skin.sys.btn.close",
        L"_skin.sys.btn.minimize",
        L"_skin.sys.btn.maxmize",
        L"_skin.sys.btn.restore",
        L"_skin.sys.menu.check",
        L"_skin.sys.menu.sep",
        L"_skin.sys.menu.arrow",
        L"_skin.sys.menu.border",
        L"_skin.sys.menu.skin",
        L"_skin.sys.icons",
        L"_skin.sys.wnd.bkgnd",
        L"_skin.sys.btn.prev",
        L"_skin.sys.btn.next",
        L"_skin.sys.spin.down",
        L"_skin.sys.spin.up",
    };
    
    ISkinObj *SApplication::GetBuiltinSkin(SYS_SKIN uID, int nScale) {
        if (m_pCurSkinPool) {
            return m_pCurSkinPool->GetSkin(__BUILDIN_SKIN_NAMES[uID], nScale);
        }
        return nullptr;
    }
    
    ISkinObj *SApplication::GetBuiltinSkin(const SStringW &strSkinName, int nScale) {
        if (m_pCurSkinPool) {
            return m_pCurSkinPool->GetSkin(strSkinName, nScale);
        }
        return nullptr;
    }
    
    bool SApplication::AddMsgLoop(SMessageLoop *pMsgLoop) {
        SAutoLock autoLock(m_cs);
        SASSERT(pMsgLoop != NULL);
        DWORD dwThreadID = ::GetCurrentThreadId();
        if(m_msgLoopMap.Lookup(dwThreadID) != NULL) {
            return false;    // in map yet
        }
        m_msgLoopMap[dwThreadID] = pMsgLoop;
        return true;
    }
    
    bool SApplication::RemoveMsgLoop() {
        SAutoLock autoLock(m_cs);
        return m_msgLoopMap.RemoveKey(::GetCurrentThreadId());
    }
    
    SMessageLoop *SApplication::GetMsgLoop(DWORD dwThreadID /*= ::GetCurrentThreadId()*/) const {
        SAutoLock autoLock(m_cs);
        const SMap<DWORD, SMessageLoop *>::CPair *p =  m_msgLoopMap.Lookup(dwThreadID);
        if(!p) {
            return NULL;
        }
        return p->m_value;
    }
    
    COLORREF SApplication::GetDefTextColor() {
        return m_colorTextDef;
    }
    COLORREF SApplication::GetDefTextGrayColor() {
        return m_colorTextGrayDef;
    }
    
    void SApplication::NotifySkinChanged() {
        if (::IsWindow(m_hMainWnd)) {
            ::SendMessage(m_hMainWnd, SOUI::UM_CHANGE_SKIN, 0, 0);
        }
    }
    
    BOOL SApplication::IsFileType(LPCTSTR pszType) {
        if (!pszType) {
            return FALSE;
        }
        return _tcsicmp(pszType, _T("file")) == 0;
    }
    
    IResProvider *SApplication::GetMatchResProvider(LPCTSTR pszType, LPCTSTR pszResName) {
        if (m_pEnvResProvider && m_pEnvResProvider->HasResource(pszType, pszResName)) {
            return m_pEnvResProvider;
        }
        
        if (m_pCurThemeResProvider && m_pCurThemeResProvider->HasResource(pszType, pszResName)) {
            return m_pCurThemeResProvider;
        }
        
        return nullptr;
    }
    
    HICON SApplication::LoadIcon(LPCTSTR pszResName, int cx, int cy, BOOL bFromFile) {
        SAutoLock lock(m_cs);
        if (bFromFile) {
            return SResLoadFromFile::LoadIcon(pszResName, cx, cy);
        } else {
            IResProvider *pResProvider = GetMatchResProvider(KTypeIcon, pszResName);
            if (!pResProvider) {
                SASSERT_FMT(false, _T("load icon failed, resource %s not found!"), pszResName);
                return NULL;
            }
            
            HICON hRet = pResProvider->LoadIcon(pszResName, cx, cy);
            SASSERT_FMT(hRet, _T("load icon failed, resource content %s not found!"), pszResName);
            return hRet;
        }
        return HICON();
    }
    
    static LPCTSTR SysCursorName2ID(LPCTSTR pszCursorName) {
        struct Cursor2ID {
            LPCTSTR szName;
            LPCTSTR szID;
        };
        static const Cursor2ID cursorMap[] = {
            { _T("arrow"), IDC_ARROW },
            { _T("ibeam"), IDC_IBEAM },
            { _T("wait"), IDC_WAIT },
            { _T("cross"), IDC_CROSS },
            { _T("uparrow"), IDC_UPARROW },
            { _T("size"), IDC_SIZE },
            { _T("sizenwse"), IDC_SIZENWSE },
            { _T("sizenesw"), IDC_SIZENESW },
            { _T("sizewe"), IDC_SIZEWE },
            { _T("sizens"), IDC_SIZENS },
            { _T("sizeall"), IDC_SIZEALL },
            { _T("no"), IDC_NO },
            { _T("hand"), IDC_HAND },
            { _T("help"), IDC_HELP },
        };
        
        for (int i = 0; i < ARRAYSIZE(cursorMap); i++) {
            if (0 == _tcsicmp(cursorMap[i].szName, pszCursorName)) {
                return cursorMap[i].szID;
            }
        }
        return NULL;
    }
    
    
    HCURSOR SApplication::LoadCursor(LPCTSTR pszResName, BOOL bFromFile) {
        SAutoLock lock(m_cs);
        if (IS_INTRESOURCE(pszResName)) {
            return ::LoadCursor(NULL, pszResName);
        } else {
            LPCTSTR pszCursorID = SysCursorName2ID(pszResName);
            if (pszCursorID) {
                return ::LoadCursor(NULL, pszCursorID);
            }
        }
        auto pPair = m_mapCachedCursor.find(pszResName);
        if (pPair != m_mapCachedCursor.end()) {
            return pPair->second;
        }
        
        HCURSOR hRet = NULL;
        if (bFromFile) {
            hRet = SResLoadFromFile::LoadCursor(pszResName);
        } else {
        
            IResProvider *pResProvider = GetMatchResProvider(KTypeCursor, pszResName);
            if (!pResProvider) {
                SASSERT_FMT(false, _T("load cursor failed, resource index %s not found!"), pszResName);
            } else {
                hRet = pResProvider->LoadCursor(pszResName);
            }
        }
        if (hRet) {
            m_mapCachedCursor[pszResName] = hRet;
        } else {
            SASSERT_FMT(false, _T("load cursor failed, resource content %s not found!"), pszResName);
        }
        return hRet;
    }
    
    HBITMAP SApplication::LoadBitmap(LPCTSTR pszResName, BOOL bFromFile) {
        SAutoLock lock(m_cs);
        if (bFromFile) {
            return SResLoadFromFile::LoadBitmap(pszResName);
        } else {
            IResProvider *pResProvider = GetMatchResProvider(KTypeBitmap, pszResName);
            if (!pResProvider) {
                SASSERT_FMT(false, _T("load bitmap failed, resource index %s not found!"), pszResName);
                return NULL;
            }
            HBITMAP hBmp = pResProvider->LoadBitmap(pszResName);
            return hBmp;
        }
    }
    
    IBitmap *SApplication::LoadImage(LPCTSTR pszType, LPCTSTR pszResName) {
        if (!pszType) {
            return NULL;
        }
        SAutoLock lock(m_cs);
        if (IsFileType(pszType)) {
            return SResLoadFromFile::LoadImage(pszResName);
        } else {
            IResProvider *pResProvider = GetMatchResProvider(pszType, pszResName);
            if (!pResProvider) {
                SASSERT_FMT(false, _T("load image failed, resource index %s:%s not found!"), pszType, pszResName);
                return NULL;
            }
            IBitmap *pRet = pResProvider->LoadImage(pszType, pszResName);
            return pRet;
        }
    }
    
    IImgX *SApplication::LoadImgX(LPCTSTR pszType, LPCTSTR pszResName) {
        SAutoLock lock(m_cs);
        if (IsFileType(pszType)) {
            return SResLoadFromFile::LoadImgX(pszResName);
        } else {
        
            IResProvider *pResProvider = GetMatchResProvider(pszType, pszResName);
            if (!pResProvider) {
                return NULL;
            }
            return pResProvider->LoadImgX(pszType, pszResName);
        }
    }
    
    IBitmap *SApplication::LoadImage2(const SStringW &strImgID) {
        SStringT strImgID2 = S_CW2T(strImgID);
        SStringTList strLst;
        int nSegs = ParseResID(strImgID2, strLst);
        if (nSegs == 2) {
            return LoadImage(strLst[0], strLst[1]);
        } else {
            return NULL;
        }
    }
    
    HICON SApplication::LoadIcon2(const SStringW &strIconID) {
        SStringT strIconID2 = S_CW2T(strIconID);
        SStringTList strLst;
        int nSegs = ParseResID(strIconID2, strLst);
        if (nSegs == 2) {
            int cx = _ttoi(strLst[1]);
            return LoadIcon(strLst[0], cx, cx);
        } else {
            return LoadIcon(strLst[0]);
        }
    }
    
    size_t SApplication::GetRawBufferSize(LPCTSTR strType, LPCTSTR pszResName) {
        SAutoLock lock(m_cs);
        if (IsFileType(strType)) {
            return SResLoadFromFile::GetRawBufferSize(pszResName);
        } else {
            IResProvider *pResProvider = GetMatchResProvider(strType, pszResName);
            if (!pResProvider) {
                return 0;
            }
            return pResProvider->GetRawBufferSize(strType, pszResName);
        }
    }
    
    BOOL SApplication::GetRawBuffer(LPCTSTR strType, LPCTSTR pszResName, LPVOID pBuf, size_t size) {
        SAutoLock lock(m_cs);
        if (IsFileType(strType)) {
            return SResLoadFromFile::GetRawBuffer(pszResName, pBuf, size);
        } else {
            IResProvider *pResProvider = GetMatchResProvider(strType, pszResName);
            if (!pResProvider) {
                return FALSE;
            }
            return pResProvider->GetRawBuffer(strType, pszResName, pBuf, size);
        }
    }
    
    bool SApplication::SetTheme(const SOUI::SStringW &ssThemeName, const SOUI::SStringW &themeType) {
    
        bool loadSkin = false;
        auto iterFind = m_mapSkinPool.find(ssThemeName);
        if (iterFind != m_mapSkinPool.end()) {
            m_pCurSkinPool = iterFind->second;
        } else {
            m_pCurSkinPool = std::make_shared< XSkinPool>();
            m_mapSkinPool[ssThemeName] = m_pCurSkinPool;
            loadSkin = true;
        }
        
        SOUI::CAutoRefPtr<SOUI::IResProvider>   pResProvider;
        auto iter = m_themeProviders.find(ssThemeName);
        if (iter != m_themeProviders.end()) {
            m_pCurThemeResProvider = iter->second;
            pResProvider = m_pCurThemeResProvider;
        } else {
        
            SOUI::CreateResProvider(SOUI::RES_FILE, (SOUI::IObjRef **)&pResProvider);
            if (!pResProvider->Init((LPARAM)ssThemeName.c_str(), 0)) {
                return false;
            }
            m_pCurThemeResProvider = pResProvider;
            m_themeProviders[ssThemeName] = pResProvider;
        }
        
        LPCTSTR pszUidef = _T("uidef:xml_init");
        BOOL bRet = FALSE;
        SStringTList strUiDef;
        if (2 != ParseResID(pszUidef, strUiDef)) {
            SLOGFMTD(_T("warning!!!! Add ResProvider Error."));
        }
        const wchar_t *defFontFace = KDefFontFace;
        size_t dwSize = pResProvider->GetRawBufferSize(strUiDef[0], strUiDef[1]);
        if (dwSize == 0) {
            SLOGFMTD(_T("warning!!!! uidef was not found in the specified resprovider"));
            return false;
        } else {
            pugi::xml_document docInit;
            SAutoBuf strXml;
            strXml.Allocate(dwSize);
            pResProvider->GetRawBuffer(strUiDef[0], strUiDef[1], strXml, dwSize);
            pugi::xml_parse_result result = docInit.load_buffer(strXml, strXml.size(), pugi::parse_default, pugi::encoding_auto);
            
            if (!result) {
                //load xml failed
                SLOGFMTD(_T("warning!!! load uidef as xml document failed"));
                return false;
            } else {
                //init named objects
                pugi::xml_node root = docInit.child(KNodeUidef, false);
                if (!root) {
                    SLOGFMTD(_T("warning!!! \"uidef\" element is not the root element of uidef xml"));
                    return false;
                } else {
                    //load named color
                    {
                        pugi::xml_document docData;
                        pugi::xml_node     nodeData = GetSourceXmlNode(root, docData, pResProvider, KNodeColor);
                        if (nodeData) {
                            m_namedColor.Init(nodeData);
                            m_colorTextDef = GetColor(L"@color/defText");
                            m_colorTextGrayDef = GetColor(L"@color/defTextGray");
                            /*if (ssThemeName.EndsWith(L"black")) {
                                m_colorTextDef = GETCOLOR(L"@color/x_btn_text_normal");
                                m_colorTextGrayDef = GETCOLOR(L"@color/x_btn_text_normal");
                            }
                            else {
                                m_colorTextDef = CR_INVALID;
                                m_colorTextGrayDef = CR_INVALID;
                            }*/
                        }
                    }
                    //load named skin
                    if(loadSkin) {
                        pugi::xml_document docData;
                        pugi::xml_node     nodeData = GetSourceXmlNode(root, docData, pResProvider, KNodeSkin);
                        if (nodeData) {
                            m_pCurSkinPool->LoadSkins(nodeData);
                        }
                    }
                    
                    
                }
            }
        }
        
        return true;
    }
    
    COLORREF SApplication::GetColor(const SStringW &strColor) {
        return m_namedColor.Get(strColor);
    }
    
    void SApplication::updateColor(const SStringW &colorKey, COLORREF colorVal) {
        m_namedColor.update(colorKey, colorVal);
    }
    
    COLORREF SApplication::GetColor(int idx) {
        return m_namedColor.Get(idx);
    }
    
    SStringW SApplication::GetString(const SStringW &strString) {
        return m_namedString.Get(strString);
    }
    
    SStringW SApplication::GetString(int idx) {
        return m_namedString.Get(idx);
    }
    
    SLayoutSize SApplication::GetLayoutSize(const SStringW &strSize) {
        return m_namedDim.Get(strSize);
    }
    
    SLayoutSize SApplication::GetLayoutSize(int idx) {
        return m_namedDim.Get(idx);
    }
    pugi::xml_node SApplication::GetCaretInfo() {
        return xmlCaret.child(L"caret");
    }
    SObjDefAttr *SApplication::GetObjDefAttr() {
        return nullptr;
    }
}//namespace SOUI